package angel.prototype.controller;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Properties;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.beans.propertyeditors.CustomNumberEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.util.WebUtils;

import angel.core.freemarker.Shrinksafe;

//import angel.core.freemarker.Shrinksafe;

@Controller
public class GenericController implements ServletContextAware {
	
	private static final Logger logger = LoggerFactory.getLogger(GenericController.class);
	private static final String UPLOAD_DIR = "{project}/tmp/";
	private ServletContext servletContext = null;
	
	//private static final String WEB_APP_HOST = "webAppHost";
//	private static final String WEB_APP_CONTEXT = "webAppContext";
//	private static final String WEB_APP_PROJECT = "project";
//	private static final String WEB_APP_SUBJECT = "subject";
//	private static final String WEB_APP_ROOT_KEY = "webAppRootKey";
//	private static final String WEB_APP_ROOT_VERSION = "webAppVersion";
	private final static Properties KEYWORDS = new Properties();
	static{
//		try{
//			if(KEYWORDS.isEmpty()){
//				KEYWORDS.load(Shrinksafe.class.getResourceAsStream("ShrinksafeKeywords.properties"));
//			}					
//		}
//		catch(IOException e){
//			logger.error("Failed to load ShrinksafeKeywords.properties",e);
//		}
	}
	public GenericController(){
		logger.info("#####GenericController has been initialized####06-Jun-2015");
	}
	
	public void setServletContext(ServletContext servletContext) {
		this.servletContext = servletContext;
		this.servletContext.setAttribute(WebUtils.TEMP_DIR_CONTEXT_ATTRIBUTE,new File(""));
	}

	@InitBinder
	public void initBinder(WebDataBinder binder,WebRequest request,Locale locale) {
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
		dateFormat.setLenient(false);
		binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));		
		binder.registerCustomEditor(Integer.class, new CustomNumberEditor(Integer.class,true));
//		binder.registerCustomEditor(Feedback.class, new PropertyEditorSupport(){
//			@Override
//			public void setAsText(String text) {
//				System.out.println("Feedback@@@@@@@@@@@@");
//				this.setValue(new Feedback());
//			}			
//		});
	}
	
	@RequestMapping(value="/v/{index}",method={RequestMethod.GET})	
	public String index1(@PathVariable("index") String index,HttpServletRequest request,HttpServletResponse response,ModelMap modelMap){		
		logger.info("request has been processed:{}/{}",request.getContextPath(),index);
		logger.info("Client from {}",request.getRemoteAddr());		
		response.setCharacterEncoding("UTF-8");
		
		this.initModelMap(modelMap, request);
		return index;
	}	
	
	@RequestMapping(value="/v/{project}/{view}",method={RequestMethod.GET})	
	public String v(@PathVariable("project") String project,@PathVariable("view") String view,HttpServletRequest request,HttpServletResponse response,ModelMap modelMap){		
		logger.info("request has been processed:{}/{}",project,view);
		logger.info("Client from {}",request.getRemoteAddr());
		this.initModelMap(modelMap, request);
		return project +"/"+view;
	}
	
//	@RequestMapping(value="/dojo/require",method={RequestMethod.POST,RequestMethod.GET})	
//	@ResponseBody
//	public String dojoRequire(@RequestParam("widget") String widget,HttpServletRequest request,HttpServletResponse response,ModelMap modelMap){
//		modelMap.put("context", request.getContextPath());		
//		response.addHeader("Access-Control-Allow-Origin", "*");  
//		logger.info("dojo require:"+widget+"," + "/dojo/"+ widget.replaceAll("\\.", "/") +".js");
//		try{
//			File file = new File(this.servletContext.getRealPath("/dojo/"+ widget.replaceAll("\\.", "/") +".js"));
//			//logger.info("dojo :"+file.getAbsolutePath());
//			//logger.info("file.exists() :"+file.exists());
//			if(file.exists()){
//				return FileUtils.readFileToString(file);	
//			}
//		}
//		catch(Exception e){
//			logger.error("error", e);
//		}
//		return "";		
//	}
	
	@RequestMapping(value="/{project}/require",method={RequestMethod.GET})	
	public String require(@PathVariable("project") String project,@RequestParam("widget") String widget,HttpServletRequest request,HttpServletResponse response,ModelMap modelMap){
		this.initModelMap(modelMap, request);
//		modelMap.put(WEB_APP_PROJECT, project);
		logger.info("request has been processed:{}/require,{}" ,project,widget);		
		//modelMap.put("widget", widget.replaceAll("\\.", "/"));
		modelMap.put("widget", widget);
		modelMap.put("widget_abbr", KEYWORDS.getProperty(widget, widget));
		modelMap.put("eager", true);
		//return project + "/" + widget.replaceAll("\\.", "/");
		return project + "/require";
	}
	
//	@RequestMapping(value="/{project}/{subject}",method={RequestMethod.POST,RequestMethod.GET})	
//	public String prototype(@PathVariable("project") String project,@PathVariable("subject") String subject,HttpServletRequest request,HttpServletResponse response,ModelMap modelMap){		
//		logger.info("request has been processed:/{}/{},modelMap:{}",project,subject,modelMap);
//		logger.info("LanguageTag="+request.getLocale().toLanguageTag());
//		logger.info("Language"+request.getLocale().toString());
//		response.setCharacterEncoding("UTF-8");
////		String preferredLangeage = request.getParameter("lang");
////		if(StringUtils.isEmpty(preferredLangeage)){
////			preferredLangeage = request.getLocale().toLanguageTag();			
////		}
//
//		this.initModelMap(modelMap, request);
//		modelMap.put(WEB_APP_PROJECT, project);
//		return project + "/"+subject;
//	}
	@RequestMapping(value="/v/{project}/preview",method={RequestMethod.POST,RequestMethod.GET})	
	public String preview(@PathVariable("project") String project,HttpServletRequest request,HttpServletResponse response,ModelMap modelMap){		
//		logger.info("request has been processed:/{}/{},modelMap:{}",project,subject,modelMap);
//		logger.info("LanguageTag="+request.getLocale().toLanguageTag());
//		logger.info("Language"+request.getLocale().toString());
//		response.setCharacterEncoding("UTF-8");
//		String preferredLangeage = request.getParameter("lang");
//		if(StringUtils.isEmpty(preferredLangeage)){
//			preferredLangeage = request.getLocale().toLanguageTag();			
//		}

		this.initModelMap(modelMap, request);
//		modelMap.put(WEB_APP_PROJECT, project);
//		modelMap.put("subject", subject);
		return project + "/preview";
	}
//	@RequestMapping(value="/{a}/{b}/{c}",method={RequestMethod.POST,RequestMethod.GET})
//	public String abcd(@PathVariable("a") String project,@PathVariable("b") String subject,@PathVariable("c") String c,HttpServletRequest request,HttpServletResponse response,ModelMap modelMap){		
//		logger.info("request has been processed:/{}/{},modelMap:{}",project,subject,modelMap);
//		logger.info("LanguageTag="+request.getLocale().toLanguageTag());
//		logger.info("Language"+request.getLocale().toString());
//		response.setCharacterEncoding("UTF-8");
////		String preferredLangeage = request.getParameter("lang");
////		if(StringUtils.isEmpty(preferredLangeage)){
////			preferredLangeage = request.getLocale().toLanguageTag();			
////		}
//
//		this.initModelMap(modelMap, request);
//		modelMap.put(WEB_APP_PROJECT, project);
//		return project + "/"+subject +"/" +c;
//	}
	
//	@RequestMapping(value="/{project}/sse/{subject}",method={RequestMethod.POST,RequestMethod.GET})	
//	public String serverSendEvent(@PathVariable("project") String project,@PathVariable("subject") String subject,HttpServletRequest request,HttpServletResponse response,ModelMap modelMap){		
//		logger.info("request has been processed:/{}/{},modelMap:{}",project,subject,modelMap);
//		logger.info("LanguageTag="+request.getLocale().toLanguageTag());
//		logger.info("Language"+request.getLocale().toString());
//		response.setCharacterEncoding("UTF-8");
//		String preferredLangeage = request.getParameter("lang");
//		if(StringUtils.isEmpty(preferredLangeage)){
//			preferredLangeage = request.getLocale().toLanguageTag();			
//		}
//
//		this.initModelMap(modelMap, request);
//		modelMap.put(WEB_APP_PROJECT, project);
//		//response.setContentType("text/event-stream");
//		response.setHeader("Content-Type", "text/event-stream");
//		response.setHeader("Cache-Control", "no-cach");
//		return project + "/"+subject;
//	}
/*20141006	
	//[#-- manifest="${project}/prototype/appCache.manifest"--]
	@RequestMapping(value="/{project}/index",method={RequestMethod.POST,RequestMethod.GET})	
	public String index(@PathVariable("project") String project,HttpServletRequest request,HttpServletResponse response,ModelMap modelMap){		
		logger.info("request has been processed:{}/{}/index, webAppVersion={}",request.getContextPath(),project,servletContext.getInitParameter("webAppVersion"));
//		logger.info("LanguageTag="+request.getLocale().toLanguageTag());
		//logger.info("Language"+request.getLocale().toString());
		logger.info("Client from {}",request.getRemoteAddr());

		response.setCharacterEncoding("UTF-8");
//		String preferredLangeage = request.getParameter("lang");
//		if(StringUtils.isEmpty(preferredLangeage)){
//			preferredLangeage = request.getLocale().toLanguageTag();			
//		}
//		modelMap.put("lang", preferredLangeage);
		this.initModelMap(modelMap, request);
		modelMap.put(WEB_APP_PROJECT, project);
		
		return project + "/index";
	}	
	
	@RequestMapping(value="/{project}/bootstrap",method={RequestMethod.POST,RequestMethod.GET})
	public String bootstrap(@PathVariable("project") String project,HttpServletRequest request,ModelMap modelMap){		
		logger.info("bootstrap:navigatorUserAgent=" +request.getParameter("navigatorUserAgent"));
		logger.info("bootstrap:navigatorAppVersion=" +request.getParameter("navigatorAppVersion"));
		logger.info("bootstrap:webAppVersion=" +request.getParameter("webAppVersion"));
		
		this.initModelMap(modelMap, request);
		modelMap.put(WEB_APP_PROJECT, project);
		return project + "/bootstrap";
	}
*/	
//	@RequestMapping(value="/{project}/require",method={RequestMethod.POST,RequestMethod.GET})	
//	public String require(@PathVariable("project") String project,@RequestParam("widget") String widget,HttpServletRequest request,ModelMap modelMap){		
//		logger.info("request has been processed:/prototype/" + widget.replace('.', '/'));
//		logger.info("definedWidgets={}",request.getParameter("definedWidgets"));
////		modelMap.put("definedWidgets", request.getParameter("definedWidgets"));
//		modelMap.put("project", request.getContextPath());
//		String shrinkSafeKey = request.getContextPath() +"/prototype/"+widget.replace('.', '/');
//		modelMap.put("shrinkSafeKey", shrinkSafeKey);
//		this.initModelMap(modelMap, request);
//		return context + "/" + widget.replace('.', '/');
//	}
	
	@RequestMapping(value="/{project}/load",method={RequestMethod.POST,RequestMethod.GET})	
	public String load(@PathVariable("project") String project,@RequestParam("target") String target,HttpServletRequest request,HttpServletResponse response,ModelMap modelMap){		
//		logger.info("loading target:" + target);
//		
//		logger.info("[TODO] validate the access right:" + target);
////		String targetPath = request.getSession().getServletContext().getRealPath(target);
//		logger.info("loading target:" + target);
//		response.setCharacterEncoding("utf-8");
		
//		logger.info("response CharacterEncoding=" + response.getCharacterEncoding());
		modelMap.put("context", request.getContextPath());
		modelMap.put("language", request.getLocale().getLanguage());
		modelMap.put("country", request.getLocale().getCountry().toLowerCase());
//		modelMap.put(WEB_APP_PROJECT, project);
		this.initModelMap(modelMap, request);
		return UPLOAD_DIR.replace("{project}", project)+target;
	}
	
	@RequestMapping(value="/{project}/save",method={RequestMethod.POST,RequestMethod.GET})
	@ResponseBody
	public String save(@PathVariable("project") String project,HttpServletRequest request,ModelMap modelMap){		
//		logger.info("request has been processed:/prototype/command/save" + "\nmodelMap:"+modelMap);	
//		String targetPath = request.getSession().getServletContext().getRealPath(tempPath);

//		logger.info("request CharacterEncoding=" + request.getCharacterEncoding());
		
		String target = request.getParameter("target");
		if(target != null){
//			targetPath = request.getSession().getServletContext().getRealPath(tempPath +target +".ftl");
		}else{
			target = request.getParameter("title");
//			targetPath = request.getSession().getServletContext().getRealPath(target);			
		}
		
		String targetPath = null;// request.getSession().getServletContext().getRealPath(tempPath + target +".ftl");
		String targetFile = UPLOAD_DIR.replace("{project}", project) + target;		
		try{
			targetPath = request.getSession().getServletContext().getRealPath(java.net.URLDecoder.decode(targetFile +".ftl","utf-8"));
			
			String fileContent = java.net.URLDecoder.decode(request.getParameter("content"),"utf-8");
			
			FileUtils.writeStringToFile(new File(targetPath), fileContent, false);
		}catch(IOException ioe){
			logger.error("failed to save", ioe);
		}
		logger.info("content has been saved to file:" + targetPath);
		
		this.initModelMap(modelMap, request);
//		modelMap.put(WEB_APP_PROJECT, project);
		//return tempPath+"/"+fileName;
		//return UPLOAD_DIR+target;
		return targetFile;
	}
	
	@RequestMapping(value="/{project}/i18n",method={RequestMethod.GET})
	@ResponseBody
	public String i18n(@PathVariable("project") String project,@RequestParam("type") String type,@RequestParam("name") String name,HttpServletRequest request,ModelMap modelMap){
		Locale locale = request.getLocale();
		String[] paths = new String[3];
		paths[0] = new StringBuilder().append("/prototype10/nls/").append(locale.getLanguage().toLowerCase()).append("-").append(locale.getCountry().toLowerCase()).append("/").append(name).toString();
		paths[1] = new StringBuilder().append("/prototype10/nls/").append(locale.getLanguage().toLowerCase()).append("/").append(name).toString();
		paths[2] = new StringBuilder().append("/prototype10/nls/").append(name).toString();
		
		boolean found = false;
		File file = null;		
		for(int i = 0; i < paths.length; i++){
			file = new File(this.servletContext.getRealPath(paths[i]));
			if(file.exists()){
				if(type.equalsIgnoreCase("url")){
					return paths[i];
				}else if(type.equalsIgnoreCase("content")){	
					try{
						return FileUtils.readFileToString(file);
					}catch(Exception e){
						e.printStackTrace();
					}
				}
				break;
			}
		}
		
		return "404.ftl";		
	}
	
	@RequestMapping(value="/{project}/command/export",method={RequestMethod.POST,RequestMethod.GET})	
	public String prototypeCommandExport(@PathVariable("project") String project,HttpServletRequest request,ModelMap modelMap){		
//		logger.info("request has been processed:/prototype/command/export" + "\nmodelMap:"+modelMap);	
		
		Enumeration<String> e = request.getParameterNames();						
		while(e.hasMoreElements()){
			String name = e.nextElement();
//			logger.debug("Parameter:"+name + "=" + request.getParameter(name));
		}
		
		modelMap.addAttribute("title",request.getParameter("title"));
		modelMap.addAttribute("content",request.getParameter("content"));
//		modelMap.put(WEB_APP_PROJECT, project);
		this.initModelMap(modelMap, request);
		return  project+"/command/export";
	}
	
	@RequestMapping(value="/{project}/command/upload",method={RequestMethod.POST})
	@ResponseBody
	public String prototypeCommandUpload(@PathVariable("project") String project,@RequestParam("version") float version,MultipartHttpServletRequest request,ModelMap modelMap){		
		logger.info("request has been processed:/{}/command/upload",project);
		String tempPath = this.UPLOAD_DIR.replace("{project}", project);
		String uploadTargetPath = request.getSession().getServletContext().getRealPath(tempPath);
		//FileUtils.
		logger.debug("temp:"+uploadTargetPath+",tempPath:"+tempPath+",contextPath:"+request.getContextPath());		
		String contextPath = request.getContextPath();
		if(contextPath.equals("")){
			contextPath = "/";
		}
		StringBuilder response = new StringBuilder();		
		response.append(version >= 1.0?"":"{file:'"); 
		//int index = 0; 
		Iterator<String> filenames = request.getFileNames();
		while(filenames.hasNext()){
			String filename = filenames.next();
//			logger.debug("filename:"+filename);
			List<MultipartFile> uploadedFiles =  request.getFiles(filename);
			//TODO:create the unique suffix for the uploaded file
			String suffix = String.valueOf(System.currentTimeMillis());
			for(MultipartFile uploadFile : uploadedFiles){				
				if(!uploadFile.isEmpty()){	
					try{
						String orgFilename = uploadFile.getOriginalFilename();
						String targetFilename = orgFilename.substring(0,orgFilename.lastIndexOf(".")) + suffix + orgFilename.substring(orgFilename.lastIndexOf("."));						
						FileUtils.copyInputStreamToFile(uploadFile.getInputStream(), new File(uploadTargetPath, targetFilename));
						//response.append("file").append(index++).append(":'").append(tempPath).append("/").append(targetFilename).append("',");
						response.append(contextPath).append(tempPath).append(targetFilename).append(",");
					}catch(IOException ioe){
						logger.error("failed to upload file:" + uploadFile.getOriginalFilename(), ioe);
					}
				}
			}
		}
		
		if(response.lastIndexOf(",") != -1){
			response.deleteCharAt(response.lastIndexOf(","));
		}
		response.append(version >= 1.0?"":"'}");
		logger.debug("response:"+response);
		return response.toString();
	}
	
//	@ExceptionHandler(Exception.class)       
//	@ResponseBody
//    public String handleException(Exception ex,HttpServletRequest request) {     
//        if(ex instanceof org.springframework.web.multipart.MaxUploadSizeExceededException){  
//            request.setAttribute("error", "file too large");  
//        }  
//        return "file too large";  
//    }  
//	/**
//	 * Refresh the component information
//	 * @param id
//	 * @param request
//	 * @param modelMap
//	 * @return
//	 */
//	@RequestMapping(value="/prototype/refresh",method={RequestMethod.POST,RequestMethod.GET})
//	@ResponseBody
//	public String refreshPrototype(@RequestParam("id") String id, HttpServletRequest request,ModelMap modelMap){
//		String json = JsonUtil.toJSON(serviceRegistry.getCrudService("component").getById(id));
//		logger.debug(json);
//		return json;
//	}
//	
//	@RequestMapping(value="/prototype/edit",method={RequestMethod.POST,RequestMethod.GET})
//	//@ResponseBody
//	public String editPrototype(@RequestParam("id") String id, HttpServletRequest request,ModelMap modelMap){				
//		//modelMap.addAttribute("root", serviceRegistry.getCrudService("component").getById(id));
//		//TODO:implement the simulation engine
//		return "prototype/edit";
//	}
//	
//	/**
//	 * Generate the runnable prototype for simulation
//	 * @param id
//	 * @param request
//	 * @param modelMap
//	 * @return
//	 */
//	@RequestMapping(value="/prototype/simulate",method={RequestMethod.POST,RequestMethod.GET})	
//	public String simulatePrototype(@RequestParam("id") String id,HttpServletRequest request,ModelMap modelMap){
//		
//		//modelMap.addAttribute("root", serviceRegistry.getCrudService("component").getById(id));
//		//TODO:implement the simulation engine
//		return "prototype/simulate";
//	}
//	
//	/**
//	 * Export the prototype source code and template 
//	 * @param id - the id of root component
//	 * @param request
//	 * @param modelMap - the model map that contains the component data
//	 * @return - the name of the tempalte to generate codes
//	 */
//	@RequestMapping(value="/prototype/export",method={RequestMethod.POST,RequestMethod.GET})	
//	public String exportPrototype(@RequestParam("id") String id,HttpServletRequest request,ModelMap modelMap){
//		
//		modelMap.addAttribute("root", serviceRegistry.getCrudService("component").getById(id));
//		//TODO:implement the export engine
//		return "prototype/export";
//	}
//	@RequestMapping(value="/{project}/feedback",method={RequestMethod.POST,RequestMethod.GET})
//	@ResponseBody//message id
//	public String feedback(@PathVariable("project") String project,@ModelAttribute("feedBack") Feedback feedback,BindingResult result,HttpServletRequest request,ModelMap modelMap){
////		new PetValidator().validate(pet, result);
////		if (result.hasErrors()) {
////		return "petForm";
////		}
////		System.out.println("######"+feedback.getName());
//		modelMap.put(WEB_APP_PROJECT, project);
//		logger.info("Feedback from [{}] email [{}] subscribe[{}] comment[{}]",feedback.getName(),feedback.getEmail(),feedback.getSubscribe(),feedback.getRemark().replaceAll("\n", " "));
//		return "{message:'i18n_msg_feedback',param:['" + feedback.getName()+"']}";
//	}
	
	private void initModelMap(ModelMap modelMap,HttpServletRequest request){
//		modelMap.put("context", request.getContextPath());		
		modelMap.put("context", "");
		modelMap.put("project", "prototype10");
		modelMap.put("webAppHost", "");
		modelMap.put("host", "http://beyond-prototype.xyz");
		modelMap.put("shrink", false);	
		modelMap.put("shrinkCached", false);
		modelMap.put("hasServerResource", false);		
		modelMap.put("applicationMode", "DEV");
//		modelMap.put(WEB_APP_CONTEXT, request.getContextPath());
//		modelMap.put("webAppRootKey", servletContext.getInitParameter("webAppRootKey"));
//		modelMap.put("webAppVersion", servletContext.getInitParameter("webAppVersion"));
		//modelMap.put("webAppHost", servletContext.getInitParameter("webAppHost"));
//		modelMap.put("webAppProjects", servletContext.getInitParameter("webAppProjects"));
		
//		modelMap.put("language", request.getLocale().getLanguage().toLowerCase());
//		modelMap.put("country", request.getLocale().getCountry().toLowerCase());
	}
}
